﻿/*
 * 作者：新生命开发团队（http://www.newlifex.com/）
 * 
 * 版权：版权所有 (C) 新生命开发团队 2002-2014
 * 
 * 修改：海洋饼干（cuteant@outlook.com）
*/

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace CuteAnt.Reflection
{
	/// <summary>动态生成代码Emit助手。仅提供扩展功能，不封装基本功能</summary>
	public static class EmitHelper
	{
		#region -- 方法 --

		/// <summary>基于Ldc_I4指令的整数推送，自动选择最合适的指令</summary>
		/// <param name="IL">指令</param>
		/// <param name="value">数值</param>
		/// <returns></returns>
		public static ILGenerator Ldc_I4(this ILGenerator IL, Int32 value)
		{
			switch (value)
			{
				case -1:
					IL.Emit(OpCodes.Ldc_I4_M1);
					return IL;

				case 0:
					IL.Emit(OpCodes.Ldc_I4_0);
					return IL;

				case 1:
					IL.Emit(OpCodes.Ldc_I4_1);
					return IL;

				case 2:
					IL.Emit(OpCodes.Ldc_I4_2);
					return IL;

				case 3:
					IL.Emit(OpCodes.Ldc_I4_3);
					return IL;

				case 4:
					IL.Emit(OpCodes.Ldc_I4_4);
					return IL;

				case 5:
					IL.Emit(OpCodes.Ldc_I4_5);
					return IL;

				case 6:
					IL.Emit(OpCodes.Ldc_I4_6);
					return IL;

				case 7:
					IL.Emit(OpCodes.Ldc_I4_7);
					return IL;

				case 8:
					IL.Emit(OpCodes.Ldc_I4_8);
					return IL;
			}
			if (value > -129 && value < 128)
				IL.Emit(OpCodes.Ldc_I4_S, (SByte)value);
			else
				IL.Emit(OpCodes.Ldc_I4, value);
			return IL;
		}

		/// <summary>基于Ldarg指令的参数加载，自动选择最合适的指令</summary>
		/// <param name="IL">指令</param>
		/// <param name="value">数值</param>
		/// <returns></returns>
		public static ILGenerator Ldarg(this ILGenerator IL, Int32 value)
		{
			switch (value)
			{
				case 0:
					IL.Emit(OpCodes.Ldarg_0);
					return IL;

				case 1:
					IL.Emit(OpCodes.Ldarg_1);
					return IL;

				case 2:
					IL.Emit(OpCodes.Ldarg_2);
					return IL;

				case 3:
					IL.Emit(OpCodes.Ldarg_3);
					return IL;
				default:
					IL.Emit(OpCodes.Ldarg, value);
					return IL;
			}
		}

		/// <summary>基于Stloc指令的弹栈，自动选择最合适的指令</summary>
		/// <param name="IL">指令</param>
		/// <param name="value">数值</param>
		/// <returns></returns>
		public static ILGenerator Stloc(this ILGenerator IL, Int32 value)
		{
			switch (value)
			{
				case 0:
					IL.Emit(OpCodes.Stloc_0);
					return IL;

				case 1:
					IL.Emit(OpCodes.Stloc_1);
					return IL;

				case 2:
					IL.Emit(OpCodes.Stloc_2);
					return IL;

				case 3:
					IL.Emit(OpCodes.Stloc_3);
					return IL;
				default:
					IL.Emit(OpCodes.Stloc, value);
					return IL;
			}
		}

		/// <summary>基于Ldloc指令的压栈，自动选择最合适的指令</summary>
		/// <param name="IL">指令</param>
		/// <param name="value">数值</param>
		/// <returns></returns>
		public static ILGenerator Ldloc(this ILGenerator IL, Int32 value)
		{
			switch (value)
			{
				case 0:
					IL.Emit(OpCodes.Ldloc_0);
					return IL;

				case 1:
					IL.Emit(OpCodes.Ldloc_1);
					return IL;

				case 2:
					IL.Emit(OpCodes.Ldloc_2);
					return IL;

				case 3:
					IL.Emit(OpCodes.Ldloc_3);
					return IL;
				default:
					IL.Emit(OpCodes.Ldloc, value);
					return IL;
			}
		}

		/// <summary>查找对象中其引用当前位于计算堆栈的字段的值。</summary>
		/// <param name="IL">指令</param>
		/// <param name="field"></param>
		/// <returns></returns>
		public static ILGenerator Ldfld(this ILGenerator IL, FieldInfo field)
		{
			IL.Emit(OpCodes.Ldfld, field);
			return IL;
		}

		/// <summary>间接加载到计算堆栈</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator Ldind(this ILGenerator IL, Type type)
		{
			if (!type.IsValueType)
				IL.Emit(OpCodes.Ldind_Ref);
			else if (type.IsEnum)
				IL.Ldind(Enum.GetUnderlyingType(type));
			else if (type == typeof(IntPtr))
				IL.Emit(OpCodes.Ldind_I4);
			else if (type == typeof(UIntPtr))
				IL.Emit(OpCodes.Ldind_I4);
			else
			{
				switch (Type.GetTypeCode(type))
				{
					case TypeCode.Boolean:
						IL.Emit(OpCodes.Ldind_I1);
						break;

					case TypeCode.SByte:
						IL.Emit(OpCodes.Ldind_I1);
						break;

					case TypeCode.Byte:
						IL.Emit(OpCodes.Ldind_U1);
						break;

					case TypeCode.Char:
						IL.Emit(OpCodes.Ldind_U2);
						break;

					case TypeCode.Int16:
						IL.Emit(OpCodes.Ldind_I2);
						break;

					case TypeCode.Int32:
						IL.Emit(OpCodes.Ldind_I4);
						break;

					case TypeCode.Int64:
						IL.Emit(OpCodes.Ldind_I8);
						break;

					case TypeCode.UInt16:
						IL.Emit(OpCodes.Ldind_U2);
						break;

					case TypeCode.UInt32:
						IL.Emit(OpCodes.Ldind_U4);
						break;

					case TypeCode.UInt64:
						IL.Emit(OpCodes.Ldind_I8);
						break;

					case TypeCode.Single:
						IL.Emit(OpCodes.Ldind_R4);
						break;

					case TypeCode.Double:
						IL.Emit(OpCodes.Ldind_R8);
						break;

					default:
						throw new HmExceptionBase("{0}不支持的类型{1}", "Ldind", type);
				}
			}
			return IL;
		}

		/// <summary>间接加载到计算堆栈</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator Stind(this ILGenerator IL, Type type)
		{
			if (!type.IsValueType)
				IL.Emit(OpCodes.Stind_Ref);
			else if (type.IsEnum)
				IL.Stind(Enum.GetUnderlyingType(type));
			else if (type == typeof(IntPtr))
				IL.Emit(OpCodes.Stind_I4);
			else if (type == typeof(UIntPtr))
				IL.Emit(OpCodes.Stind_I4);
			else
			{
				switch (Type.GetTypeCode(type))
				{
					case TypeCode.Boolean:
						IL.Emit(OpCodes.Stind_I1);
						break;

					case TypeCode.SByte:
						IL.Emit(OpCodes.Stind_I1);
						break;

					case TypeCode.Byte:
						IL.Emit(OpCodes.Stind_I1);
						break;

					case TypeCode.Char:
						IL.Emit(OpCodes.Stind_I2);
						break;

					case TypeCode.Int16:
						IL.Emit(OpCodes.Stind_I2);
						break;

					case TypeCode.Int32:
						IL.Emit(OpCodes.Stind_I4);
						break;

					case TypeCode.Int64:
						IL.Emit(OpCodes.Stind_I8);
						break;

					case TypeCode.UInt16:
						IL.Emit(OpCodes.Stind_I2);
						break;

					case TypeCode.UInt32:
						IL.Emit(OpCodes.Stind_I4);
						break;

					case TypeCode.UInt64:
						IL.Emit(OpCodes.Stind_I8);
						break;

					case TypeCode.Single:
						IL.Emit(OpCodes.Stind_R4);
						break;

					case TypeCode.Double:
						IL.Emit(OpCodes.Stind_R8);
						break;

					default:
						throw new HmExceptionBase("{0}不支持的类型{1}", "Stind", type);
				}
			}
			return IL;
		}

		/// <summary>将位于指定数组索引处的包含对象引用的元素作为 O 类型（对象引用）加载到计算堆栈的顶部。</summary>
		/// <param name="IL">指令</param>
		/// <returns></returns>
		public static ILGenerator Ldelem_Ref(this ILGenerator IL)
		{
			IL.Emit(OpCodes.Ldelem_Ref);
			return IL;
		}

		/// <summary>用计算堆栈上的对象 ref 值（O 类型）替换给定索引处的数组元素。</summary>
		/// <param name="IL">指令</param>
		/// <returns></returns>
		public static ILGenerator Stelem_Ref(this ILGenerator IL)
		{
			IL.Emit(OpCodes.Stelem_Ref);
			return IL;
		}

		/// <summary>把一个类型转为指定类型，值类型装箱，引用类型直接Cast</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator CastFromObject(this ILGenerator IL, Type type)
		{
			if (type == null) throw new ArgumentNullException("type");
			if (type != typeof(Object))
			{
				if (type.IsValueType)
					IL.Emit(OpCodes.Unbox_Any, type);
				else
					IL.Emit(OpCodes.Castclass, type);
			}
			return IL;
		}

		/// <summary>装箱</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator BoxIfValueType(this ILGenerator IL, Type type)
		{
			if (type == null) throw new ArgumentNullException("type");
			if (type.IsValueType && type != typeof(void)) IL.Emit(OpCodes.Box, type);
			return IL;
		}

		/// <summary>调用</summary>
		/// <param name="IL">指令</param>
		/// <param name="method"></param>
		/// <returns></returns>
		public static ILGenerator Call(this ILGenerator IL, MethodInfo method)
		{
			if (method.IsStatic || method.DeclaringType.IsValueType || !method.IsVirtual)
				IL.EmitCall(OpCodes.Call, method, null);
			else
				IL.EmitCall(OpCodes.Callvirt, method, null);

			//if (method.IsVirtual)
			//    IL.EmitCall(OpCodes.Callvirt, method, null);
			//else
			//    IL.EmitCall(OpCodes.Call, method, null);
			return IL;
		}

		/// <summary>加载空</summary>
		/// <param name="IL">指令</param>
		/// <returns></returns>
		public static ILGenerator Ldnull(this ILGenerator IL)
		{
			IL.Emit(OpCodes.Ldnull);
			return IL;
		}

		/// <summary>返回</summary>
		/// <param name="IL">指令</param>
		/// <returns></returns>
		public static ILGenerator Ret(this ILGenerator IL)
		{
			IL.Emit(OpCodes.Ret);
			return IL;
		}

		#endregion

		#region -- 创建对象 --

		/// <summary>创建值类型，对象位于栈上</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator NewValueType(this ILGenerator IL, Type type)
		{
			// 声明目标类型的本地变量
			//Int32 index = IL.DeclareLocal(type).LocalIndex;
			IL.DeclareLocal(type);

			// 加载地址
			IL.Emit(OpCodes.Ldloca_S, 0);

			// 创建对象
			IL.Emit(OpCodes.Initobj, type);

			// 加载对象
			IL.Emit(OpCodes.Ldloc_0);
			return IL;
		}

		/// <summary>创建数组，参数必须是Object[]</summary>
		/// <param name="IL">指令</param>
		/// <param name="type">类型</param>
		/// <returns></returns>
		public static ILGenerator NewArray(this ILGenerator IL, Type type)
		{
			IL.Emit(OpCodes.Newarr, type);
			return IL;
		}

		/// <summary>创建对象</summary>
		/// <param name="IL">指令</param>
		/// <param name="constructor"></param>
		/// <returns></returns>
		public static ILGenerator NewObj(this ILGenerator IL, ConstructorInfo constructor)
		{
			IL.Emit(OpCodes.Newobj, constructor);
			return IL;
		}

		#endregion

		#region -- 复杂方法 --

		/// <summary>为引用参数声明本地变量</summary>
		/// <param name="IL">指令</param>
		/// <param name="method"></param>
		/// <returns></returns>
		public static ILGenerator CreateLocalsForByRefParams(this ILGenerator IL, MethodBase method)
		{
			Int32 firstParamIndex = method.IsStatic ? 0 : 1;
			Int32 refParams = 0;
			ParameterInfo[] ps = method.GetParameters();

			for (Int32 i = 0; i < ps.Length; i++)
			{
				// 处理引用类型参数
				if (!ps[i].ParameterType.IsByRef) { continue; }
				Type type = ps[i].ParameterType.GetElementType();
				IL.DeclareLocal(type);

				// 处理输出类型
				if (ps[i].IsOut)
				{
					IL.Ldarg(firstParamIndex)
							.Ldc_I4(i)
							.Ldelem_Ref()
							.CastFromObject(type)
							.Stloc(refParams);
				}
				refParams++;
			}
			return IL;
		}

		/// <summary>将引用参数赋值到数组</summary>
		/// <param name="IL">指令</param>
		/// <param name="method"></param>
		/// <returns></returns>
		public static ILGenerator AssignByRefParamsToArray(this ILGenerator IL, MethodBase method)
		{
			Int32 firstParamIndex = method.IsStatic ? 0 : 1;
			Int32 refParam = 0;
			ParameterInfo[] ps = method.GetParameters();

			for (Int32 i = 0; i < ps.Length; i++)
			{
				// 处理引用类型参数
				if (!ps[i].ParameterType.IsByRef) { continue; }
				IL.Ldarg(firstParamIndex)
						.Ldc_I4(i)
						.Ldloc(refParam++)
						.BoxIfValueType(ps[i].ParameterType.GetElementType())
						.Stelem_Ref();
			}
			return IL;
		}

		/// <summary>将参数压栈</summary>
		/// <param name="IL">指令</param>
		/// <param name="method"></param>
		/// <returns></returns>
		public static ILGenerator PushParamsOrLocalsToStack(this ILGenerator IL, MethodBase method)
		{
			Int32 firstParamIndex = method.IsStatic ? 0 : 1;
			Int32 refParam = 0;
			ParameterInfo[] ps = method.GetParameters();

			for (Int32 i = 0; i < ps.Length; i++)
			{
				if (ps[i].ParameterType.IsByRef)
				{
					IL.Emit(OpCodes.Ldloc_S, refParam++);
				}
				else
				{
					IL.Ldarg(firstParamIndex)
							.Ldc_I4(i)
							.Ldelem_Ref()
							.CastFromObject(ps[i].ParameterType);
				}
			}
			return IL;
		}

		/// <summary>将指定参数位置的数组参数按照方法参数要求压栈</summary>
		/// <param name="IL">指令</param>
		/// <param name="paramIndex"></param>
		/// <param name="method"></param>
		/// <returns></returns>
		public static ILGenerator PushParams(this ILGenerator IL, Int32 paramIndex, MethodBase method)
		{
			ParameterInfo[] ps = method.GetParameters();

			for (Int32 i = 0; i < ps.Length; i++)
			{
				IL.Ldarg(paramIndex)
						.Ldc_I4(i)
						.Ldelem_Ref()
						.CastFromObject(ps[i].ParameterType);
			}
			return IL;
		}

		/// <summary>将指定参数位置的数组参数一个个压栈</summary>
		/// <param name="IL">指令</param>
		/// <param name="paramIndex"></param>
		/// <param name="paramTypes"></param>
		/// <returns></returns>
		public static ILGenerator PushParams(this ILGenerator IL, Int32 paramIndex, Type[] paramTypes)
		{
			if (paramTypes == null || paramTypes.Length < 1) return IL;

			for (Int32 i = 0; i < paramTypes.Length; i++)
			{
				IL.Ldarg(paramIndex)
						.Ldc_I4(i)
						.Ldelem_Ref()
						.CastFromObject(paramTypes[i]);
			}
			return IL;
		}

		#endregion
	}
}